//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <aura.h>
#include "win32.h"
#include <stdio.h>
#include <string.h>
#define FALSE 0


void AnsiToUnic(
    /* [out] */ Aura_PWChar desc,
    /* [in] */ char *src)
{
    while (*src) {
        *desc++ = (Aura_WChar)*src++;
    }
    *desc = 0;
}

Aura_ECode Win32ErrorToECodeError(
    /* [in] */ DWORD dwError)
{
    Aura_ECode ec = AURA_NOERROR;

    switch(dwError) {
    case ERROR_SUCCESS:
        ec = AURA_NOERROR;
        break;
    case ERROR_ALREADY_EXISTS:
        ec = AURA_E_ALREADY_EXIST;
        break;
    case ERROR_BROKEN_PIPE:
    case ERROR_NO_DATA:
        ec = AURA_E_PROCESS_ALREADY_EXITED;
        break;
    case ERROR_FILE_NOT_FOUND:
    case ERROR_MOD_NOT_FOUND:
        ec = AURA_E_FILE_NOT_FOUND;
        break;
    case ERROR_NOT_OWNER:
        ec = AURA_E_NOT_OWNER;
        break;
    case ERROR_COMMITMENT_LIMIT:
        ec = AURA_E_OUT_OF_MEMORY;
        break;
    case ERROR_INVALID_PARAMETER:
        ec = AURA_E_INVALID_ARGUMENT;
        break;
    case ERROR_MORE_DATA:
        ec = AURA_E_BUFFER_TOO_SMALL;
        break;
    default:
        Aura_printf(" (MS)Last Error: %d (0x%08x)\n", dwError, dwError);
        ec = AURA_E_FAIL;
    }

    return ec;
}

Aura_ECode GetLastECodeError()
{
    DWORD dwErr = GetLastError();
    return Win32ErrorToECodeError(dwErr);
}

Aura_Handle g_hTargetModule = NULL;
void CleanDll(void)
{
    if(NULL != g_hTargetModule)
        AuraModule_Unload(g_hTargetModule);
}

int main(int argc, char **argv)
{
    Aura_WChar pwszFile[256] = {0};
    typedef void (*MAIN_CRT_STARTUP)(void);

#ifdef _GNUC
    // whether "-debug"
    bool bDbg = false;
    if(strcmp(argv[1], "-debug") == 0) {
        AnsiToUnic(pwszFile, argv[2]);
        bDbg = true;
    } else {
#endif
        AnsiToUnic(pwszFile, argv[1]);
#ifdef _GNUC
    }
#endif

    Aura_ProcessId CurrentProcessId;
    AuraProcess_GetCurrentId(&CurrentProcessId);

    wchar_t processIdBuf[20] = {0};
    _itow(CurrentProcessId, processIdBuf, 10);

    wchar_t eventName[256] = {0};
    wcscat(eventName, L"ELAURA::LOADER::");
    wcscat(eventName, processIdBuf);

    HANDLE hLoaderEvent = CreateEventW(NULL, FALSE, FALSE, eventName);
    if(!hLoaderEvent) {
        return GetLastECodeError();
    }

    //TODO should be placed at ExitProcess func
#ifdef _GNUC
    if(!bDbg)
#endif
        atexit(CleanDll);

    Aura_ModuleLoadFlags flag = 1;

#ifdef _GNUC
    if(bDbg) flag = 99; // debug flag
#endif

    Aura_Handle hTargetModule;
    Aura_ECode ec = AuraModule_Load((Aura_PWChar)pwszFile, flag, &hTargetModule);
    if(AURA_FAILED(ec)) {
        CloseHandle(hLoaderEvent);
        return ec;
    }

    g_hTargetModule = hTargetModule;
    Aura_printf("%s has bean loaded at address 0x%x \n", argv[0], hTargetModule);

    Aura_UInt32 entryPoint;
    ec = AuraModule_GetInformation(CurrentProcessId, hTargetModule, NULL, &entryPoint, NULL);
    if(AURA_FAILED(ec)) {
        AuraModule_Unload(hTargetModule);
        CloseHandle(hLoaderEvent);
        return ec;
    }

    MAIN_CRT_STARTUP pEntryAddr = (MAIN_CRT_STARTUP)entryPoint;
    if(AURA_NULL == pEntryAddr){
        AuraModule_Unload(hTargetModule);
        CloseHandle(hLoaderEvent);
        return AURA_E_BAD_EXE_FORMAT;
    }

    SetEvent(hLoaderEvent);
    CloseHandle(hLoaderEvent);

#ifdef _GNUC
    if(bDbg) asm("int $3");
    // We will start debugging here.
#endif

    pEntryAddr();
    assert(0);
    return 0;
}
